fix: chatwoot stability, webhook crashes and WhatsApp LID support#2274
Closed
Vitordotpy wants to merge 3 commits intoEvolutionAPI:developfrom
Closed
fix: chatwoot stability, webhook crashes and WhatsApp LID support#2274Vitordotpy wants to merge 3 commits intoEvolutionAPI:developfrom
Vitordotpy wants to merge 3 commits intoEvolutionAPI:developfrom
Conversation
Contributor
Reviewer's GuideStrengthens Chatwoot, Typebot, and WhatsApp Baileys integrations by preserving full JIDs (including @lid), hardening Chatwoot contact lookup and import flows against 422/database failures, and preventing webhook crashes from invalid participant IDs. Sequence diagram for Chatwoot contact creation with 422 fallback by identifiersequenceDiagram
actor BotEngine
participant ChatwootService
participant ChatwootAPI
BotEngine->>ChatwootService: createContact(instance, jid)
ChatwootService->>ChatwootAPI: POST contacts (identifier = jid)
alt contact created successfully
ChatwootAPI-->>ChatwootService: 201 Created contact
ChatwootService->>ChatwootAPI: POST addLabelToContact(inbox, contactId)
ChatwootService-->>BotEngine: contact
else 422 identifier has already been taken
ChatwootAPI-->>ChatwootService: 422 Unprocessable Entity
ChatwootService->>ChatwootService: check error.status and error.message
ChatwootService->>ChatwootService: findContactByIdentifier(instance, jid)
ChatwootService->>ChatwootAPI: GET contacts/search q=jid
alt contact found in search payload
ChatwootAPI-->>ChatwootService: contact.data.payload[0]
ChatwootService->>ChatwootAPI: POST addLabelToContact(inbox, contactId)
ChatwootService-->>BotEngine: existingContact
else search empty
ChatwootAPI-->>ChatwootService: empty result
ChatwootService->>ChatwootAPI: POST contacts/filter identifier == jid
alt contact found in filter payload
ChatwootAPI-->>ChatwootService: contact.payload[0]
ChatwootService->>ChatwootAPI: POST addLabelToContact(inbox, contactId)
ChatwootService-->>BotEngine: existingContact
else still not found
ChatwootAPI-->>ChatwootService: empty result
ChatwootService-->>BotEngine: null
end
end
else other error
ChatwootAPI-->>ChatwootService: error
ChatwootService->>ChatwootService: logger.error Error creating contact
ChatwootService-->>BotEngine: null
end
Sequence diagram for WhatsApp GROUP_PARTICIPANTS_UPDATE normalizationsequenceDiagram
participant WhatsAppWebhook
participant BaileysStartupService
participant WebhookConsumer
WhatsAppWebhook-->>BaileysStartupService: GROUP_PARTICIPANTS_UPDATE body
BaileysStartupService->>BaileysStartupService: for each participantId
BaileysStartupService->>BaileysStartupService: normalizePhoneNumber(id)
BaileysStartupService->>BaileysStartupService: String(id || '').split('@')[0]
BaileysStartupService-->>WebhookConsumer: sanitizedParticipantId
WebhookConsumer->>WebhookConsumer: process update without crash
Class diagram for updated chatbot and WhatsApp services JID handlingclassDiagram
class BaseChatbotService {
<<abstract>>
+sendMedia(instance, remoteJid, mediaType, url, altText, settings) void
+sendText(instance, remoteJid, message, linkPreview, settings) void
}
class TypebotService {
+processListMessage(instance, formattedText, remoteJid) void
+processButtonMessage(instance, formattedText, remoteJid) void
}
class ChatwootService {
+createContact(instance, jid) any
+findContact(instance, phoneNumber) any
+findContactByIdentifier(instance, identifier) any
+getNumberFromRemoteJid(remoteJid) string
+startImportHistoryMessages(instance) void
+handleGroupMessage(body) void
}
class BaileysStartupService {
+handleGroupParticipantsUpdate(body) void
+normalizePhoneNumber(id) string
}
BaseChatbotService <|-- TypebotService
class ChatwootImport {
+updateMessageSourceID(messageId, sourceId) Promise~void~
}
class InstanceDto
ChatwootService --> InstanceDto
ChatwootService --> ChatwootImport
TypebotService --> InstanceDto
BaileysStartupService --> InstanceDto
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Contributor
There was a problem hiding this comment.
Hey there - I've reviewed your changes - here's some feedback:
- The new
findContactByIdentifiermethod contains several near-duplicate response-shape checks (contact.data.payload,contact.payload,contactByAttr.payload,contactByAttr.data.payload); consider centralizing this into a small helper that normalizes a Chatwoot response to a singlepayloadarray to reduce branching and future maintenance overhead. - In
getNumberFromRemoteJid, the function now guards against falsy input but the signature still declaresremoteJid: string; it would be clearer to either widen the type (e.g.,string | null | undefined) or move the null/undefined handling to the call sites so the type matches actual expectations. - The repeated inline logic for deriving
rawPhoneNumberfrombody.keyin ChatwootService (checkingaddressingMode === 'lid',fromMe,participantAlt, etc.) would be easier to reason about and less error-prone if factored into a shared helper that encapsulates the JID-to-phone extraction rules in one place.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new `findContactByIdentifier` method contains several near-duplicate response-shape checks (`contact.data.payload`, `contact.payload`, `contactByAttr.payload`, `contactByAttr.data.payload`); consider centralizing this into a small helper that normalizes a Chatwoot response to a single `payload` array to reduce branching and future maintenance overhead.
- In `getNumberFromRemoteJid`, the function now guards against falsy input but the signature still declares `remoteJid: string`; it would be clearer to either widen the type (e.g., `string | null | undefined`) or move the null/undefined handling to the call sites so the type matches actual expectations.
- The repeated inline logic for deriving `rawPhoneNumber` from `body.key` in ChatwootService (checking `addressingMode === 'lid'`, `fromMe`, `participantAlt`, etc.) would be easier to reason about and less error-prone if factored into a shared helper that encapsulates the JID-to-phone extraction rules in one place.
## Individual Comments
### Comment 1
<location> `src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts:349-351` </location>
<code_context>
return contact;
} catch (error) {
+ if (
+ (error.status === 422 || error.response?.status === 422) &&
+ (error.message?.includes('taken') || error.response?.data?.message?.includes('taken')) &&
+ jid
+ ) {
</code_context>
<issue_to_address>
**issue (bug_risk):** Relying on `'taken'` substring in the error message is brittle and may misclassify errors.
This 422 path depends on `error.message?.includes('taken')` / `error.response?.data?.message?.includes('taken')`, which tightly couples behavior to a particular English string from Chatwoot. That can silently break with localization, wording changes, or different 422 variants. Prefer branching on a structured signal from Chatwoot (e.g., error code, attribute, or a specific payload field). If that’s not available, at least narrow this condition (e.g., to cases where the failing field is the identifier/email) so unrelated 422s containing the word `taken` don’t incorrectly hit this path.
</issue_to_address>
### Comment 2
<location> `src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts:2534-2538` </location>
<code_context>
}
public getNumberFromRemoteJid(remoteJid: string) {
- return remoteJid.replace(/:\d+/, '').split('@')[0];
+ if (!remoteJid) {
+ return '';
+ }
+ return remoteJid.replace(/:\d+/, '').replace('@s.whatsapp.net', '').replace('@g.us', '').replace('@lid', '');
}
</code_context>
<issue_to_address>
**issue:** The new `getNumberFromRemoteJid` implementation may fail to strip unknown JID suffixes and changes behavior for some inputs.
The previous `.split('@')[0]` stripped any suffix; the new version only handles three specific ones, so JIDs like `@broadcast` or future/custom domains will now include the suffix in the "number", which may break callers expecting a pure phone number. You could keep the generic stripping while still handling the numeric resource explicitly, e.g.:
```ts
if (!remoteJid) return '';
const cleaned = remoteJid.replace(/:\d+/, '');
return cleaned.split('@')[0];
```
or apply your specific replacements first and then still `split('@')[0]` as a final safeguard. Also, returning `''` instead of `undefined`/`null` for falsy `remoteJid` changes the function’s contract, so it’s worth checking existing callers for assumptions about that value.
</issue_to_address>
### Comment 3
<location> `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:1616-1620` </location>
<code_context>
// Helper to normalize participantId as phone number
- const normalizePhoneNumber = (id: string): string => {
+ const normalizePhoneNumber = (id: string | any): string => {
// Remove @lid, @s.whatsapp.net suffixes and extract just the number part
- return id.split('@')[0];
+ return String(id || '').split('@')[0];
};
</code_context>
<issue_to_address>
**suggestion:** The `normalizePhoneNumber` parameter type `string | any` is misleading and undermines type safety.
Since the implementation already does `String(id || '')`, the function is designed to handle non-string/nullable inputs. Typing the parameter as `string | any` collapses to `any` and removes useful type checking. Use a more accurate type such as `id: string | null | undefined` (or `unknown` if you truly expect arbitrary values) so call sites are still type-checked while your runtime fallback remains valid.
```suggestion
// Helper to normalize participantId as phone number
const normalizePhoneNumber = (id: string | null | undefined): string => {
// Remove @lid, @s.whatsapp.net suffixes and extract just the number part
return String(id || '').split('@')[0];
};
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts
Outdated
Show resolved
Hide resolved
src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts
Outdated
Show resolved
Hide resolved
Contributor
Author
|
Criei uma nova PR com mais fixes além desses, vou fechar essa daqui |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This Pull Request resolves critical stability issues and functional bugs in the Evolution API, focusing on Chatwoot integration robustness and support for modern WhatsApp identifiers (LID). The fixes ensure reliable message processing, prevent application crashes due to webhook errors, and normalize identifier handling across services.
Fixes:
WhatsApp LID (Linked Identity Device) Support:
Problem: Messages originating from users with the new @lid identifier format were failing in Typebot and general message sending. The codebase aggressively stripped the domain using .split('@')[0], causing the API to lose the valid JID required for these specific identifiers.
Solution: Updated BaseChatbotService, TypebotService, and ChatwootService logic to preserve the full JID when necessary. Refactored getNumberFromRemoteJid to safely handle and clean complex JID formats (e.g., removing device suffixes like :27 while keeping the user ID intact).
Chatwoot Integration Stability (Duplicate Identifiers & Participant Errors):
Problem: The Chatwoot integration suffered from two main issues: crashes due to participantAlt being undefined when parsing message keys, and "Identifier has already been taken" (422) errors during contact creation when a contact existed but wasn't initially found.
Solution: Added safe access checks for participantAlt to prevent runtime errors. Implemented a fallback mechanism in createContact to recover the existing contact by identifier if creation fails due to duplication, ensuring the flow continues without error.
Webhook Crashes on Group Participant Updates:
Problem: The GROUP_PARTICIPANTS_UPDATE webhook event caused the application to crash when processing participant IDs that were not strings (undefined or null) in the normalizePhoneNumber function.
Solution: Added strict type coercion (String(id || '')) in WhatsappBaileysService to ensure input is always a string before processing, preventing the crash.
Unhandled Database Rejections in Chatwoot Import:
Problem: The chatwoot_import feature caused unhandled promise rejections crashing the process when the separate Postgres connection was missing or failed.
Solution: Wrapped the updateMessageSourceID and connection logic in proper try/catch blocks to gracefully handle database connectivity issues without bringing down the main application.
Summary by Sourcery
Improve system stability and Chatwoot integration reliability by identifier handling, and error resilience.
Bug Fixes:
Summary by Sourcery
Improve WhatsApp and Chatwoot integrations to better handle modern JID formats, avoid crashes, and gracefully recover from external and database errors.
Bug Fixes:
Enhancements: